<h2 style="text-align: center">多生物特征野生动物知识图谱</h2>

<!DOCTYPE html>
<meta charset="utf-8">
<link type="text/css" rel="styleSheet" href="css/index.css"/>
<link type="text/css" rel="styleSheet" href="css/bootstrap.min.css"/>
<body style="background-color:rgb(255,255,255)">
<div id="mask" style="width: 100%;height:100%;">
  <div id="mask-header">
    <button>退出</button>
  </div>
  <div id="mask-body"></div>
</div>
<div class = "mapAndAlerm" style="overflow: hidden;">
  <input id="keyword"  style="float:left;width:1000px;height:40px;margin-left: 10px" class="form-control">
  <button id="search" style="float:left;width:60px;height:40px;" title="搜索" class="btn btn-info">搜索</button>
  <button id="fullscreen" style="float:left;width:60px;height:40px;" title="全屏显示" class="btn btn-success">全屏</button>
  <button id="download" style="float:left;width:80px;height:40px;" title="下载" class="btn btn-warning">图片下载</button>
  <button id="export" style="float:left;width:80px;height:40px;" title="导出" class="btn btn-danger">导出数据</button>
  <a id="exportTag" href="" style="display: none"></a>

  <div  id="graphics" style="height:1000px;width: 2000px"></div>
</div>
<script src="js/d3.v3.min.js"></script>
<script src="js/jquery-1.8.3.min.js"></script>
<script src="js/saveSvgAsPng.js"></script>
</body>
<script>

  //下载
  function downloadFile(fileName, content) {
    var aLink = document.createElement('a');
    var blob = this.base64ToBlob(content); //new Blob([content]);
    var evt = document.createEvent("HTMLEvents");
    evt.initEvent("click", true, true);//initEvent 不加后两个参数在FF下会报错  事件类型，是否冒泡，是否阻止浏览器的默认行为
    aLink.download = fileName;
    aLink.href = URL.createObjectURL(blob);
    // aLink.dispatchEvent(evt);
    aLink.click()
  }
  //base64转blob
  function base64ToBlob(code) {
    var parts = code.split(';base64,');
    var contentType = parts[0].split(':')[1];
    var raw = window.atob(parts[1]);
    var rawLength = raw.length;

    var uInt8Array = new Uint8Array(rawLength);

    for (var i = 0; i < rawLength; ++i) {
      uInt8Array[i] = raw.charCodeAt(i);
    }
    return new Blob([uInt8Array], {type: contentType});
  }

  function mychart_first(graphics,graphicsid,linkss,title)
  {
    $("#mask-body").width($(document.body).width()-100);
    $("#mask-header").width($(document.body).width()-100);
    $("#mask-header>button").click(function(){
      //退出全屏
      $("#mask").css('display',"none");
      //重新刷新一次
      window.location.reload();
    });
    var links = [];
    for (var i = 0; i < linkss.length; i++) {
      links[i] = {
        source: linkss[i].source,
        target: linkss[i].target,
        rela:linkss[i].rela
      };
    }
    var nodes = {};

    links.forEach(function(link) {
      link.source = nodes[link.source] || (nodes[link.source] = {name: link.source});
      link.target = nodes[link.target] || (nodes[link.target] = {name: link.target});
    });

    var div = document.getElementById(graphics);
    var height = div.clientHeight;
    var width = div.clientWidth;
    var curPos_x, curPos_y, mousePos_x, mousePos_y;
    var isMouseDown=false, oldScale = 1;
    var viewBox_x = 0, viewBox_y = 0;

    var force = d3.layout.force()//layout将json格式转化为力学图可用的格式
            .nodes(d3.values(nodes))//设定节点数组
            .links(links)//设定连线数组
            .size([width, height])//作用域的大小
            .linkDistance(120)//连接线长度
            .charge(-1500)//顶点的电荷数。该参数决定是排斥还是吸引，数值越小越互相排斥
            .on("tick", tick)//指时间间隔，隔一段时间刷新一次画面
            .start();//开始转换
    var drag = force.drag()
            .on("dragstart", dragstart)
            .on("dragend",dragend);

    d3.select(graphicsid).select("svg").remove(); //重点 把上一次添加的svg 节点进行删除 每次只保留一个svg dom
    var svg = d3.select(graphicsid).append("svg")
            .attr("width", width)
            .attr("height", height)
            .call(d3.behavior.zoom()
                    .scaleExtent([0.1, 10])
                    .on("zoom", function () {
                      if (oldScale !== d3.event.scale) {
                        var scale = oldScale / d3.event.scale;
                        oldScale = d3.event.scale;
                        viewBox_x = curPos_x - scale * (curPos_x - viewBox_x);
                        viewBox_y = curPos_y - scale * (curPos_y - viewBox_y);
                        svg.attr("viewBox", viewBox_x + " " + viewBox_y + " " + width / oldScale + " " + height / oldScale);
                      }
                    }));

    svg.on("mousedown", function () {
      if (d3.event.defaultPrevented) {
        return;
      }
      isMouseDown = true;
      mousePos_x = d3.mouse(this)[0];
      mousePos_y = d3.mouse(this)[1];
    });

    svg.on("mouseup", function () {
      if (d3.event.defaultPrevented) {
        return;
      }
      isMouseDown = false;

    });

    svg.on("mousemove", function () {
      if (d3.event.defaultPrevented) {
        return;
      }
      curPos_x = d3.mouse(this)[0];
      curPos_y = d3.mouse(this)[1];
      if (isMouseDown) {
        viewBox_x = viewBox_x - d3.mouse(this)[0] + mousePos_x;
        viewBox_y = viewBox_y - d3.mouse(this)[1] + mousePos_y;
        svg.attr("viewBox", viewBox_x + " " + viewBox_y + " " + width / oldScale + " " + height / oldScale);
      }
    });

    //箭头
    var marker=
            svg.append("marker")
                    //.attr("id", function(d) { return d; })
                    .attr("id", "resolved")
                    //.attr("markerUnits","strokeWidth")//设置为strokeWidth箭头会随着线的粗细发生变化
                    .attr("markerUnits","userSpaceOnUse")
                    .attr("viewBox", "0 -5 10 10")//坐标系的区域
                    .attr("refX",38)//箭头坐标
                    .attr("refY", -1)
                    .attr("markerWidth", 10)//标识的大小
                    .attr("markerHeight", 10)
                    .attr("orient", "auto")//绘制方向，可设定为：auto（自动确认方向）和 角度值
                    .attr("stroke-width",2)//箭头宽度
                    .append("path")
                    .attr("d", "M0,-5L10,0L0,5")//箭头的路径
                    .attr('fill','#aaa');//箭头颜色
    //设置连接线
    var edges_line = svg.selectAll(".edgepath")
            .data(force.links())
            .enter()
            .append("path")
            .attr({
              'd': function(d) {return 'M '+d.source.x+' '+d.source.y+' L '+ d.target.x +' '+d.target.y},
              'class':'edgepath',
              'id':function(d,i) {return 'edgepath'+i;}})
            .style("stroke",function(d){
              return "#BBB";
            })
            .style("pointer-events", "none")
            .style("stroke-width",0.5)//线条粗细
            .attr("marker-end", "url(#resolved)" );//根据箭头标记的id号标记箭头

    var edges_text = svg.append("g").selectAll(".edgelabel")
            .data(force.links())
            .enter()
            .append("text")
            .style("pointer-events", "none")
            //.attr("class","linetext")
            .attr({  'class':'edgelabel',
              'id':function(d,i){return 'edgepath'+i;},
              'dx':80,
              'dy':0
              //'font-size':10,
              //'fill':'#aaa'
            });

    //设置线条上的文字
    edges_text.append('textPath')
            .attr('xlink:href',function(d,i) {return '#edgepath'+i})
            .style("pointer-events", "none")
            .text(function(d){return d.rela;});

    //圆圈
    var circle = svg.append("g").selectAll("circle")
            .data(force.nodes())//表示使用force.nodes数据
            .enter().append("circle")
            .style("fill",function(node){
              return "#40df29";
            })
            .style('stroke',function(node){
              return "#68AEDD";
            })
            .attr("r", 28)//设置圆圈半径
            .on("click",function(node){
              //单击时让连接线加粗
              edges_line.style("stroke-width",function(line){
                console.log(line);
                if(line.source.name==node.name || line.target.name==node.name){
                  return 8; //线条加粗
                }else{
                  return 0.5;
                }
              });
              //d3.select(this).style('stroke-width',2);
            })
            .call(force.drag);//

    //圆圈的提示文字
    circle.append("svg:title")
            .text(function(node) {

            });
    var text = svg.append("g").selectAll("text")
            .data(force.nodes())
            //返回缺失元素的占位对象（placeholder），指向绑定的数据中比选定元素集多出的一部分元素。
            .enter()
            .append("text")
            .attr("dy", ".35em")
            .attr("text-anchor", "middle")//在圆圈中加上数据
            .style('fill',function(node){
              return "#090909";
            }).attr('x',function(d){
              // console.log(d.name+"---"+ d.name.length);
              var re_en = /[a-zA-Z]+/g;
              //如果是全英文，不换行
              if(d.name.match(re_en)){
                d3.select(this).append('tspan')
                        .attr('x',0)
                        .attr('y',2)
                        .text(function(){return d.name;});
              }
              //如果小于四个字符，不换行
              else if(d.name.length<=4){
                d3.select(this).append('tspan')
                        .attr('x',0)
                        .attr('y',2)
                        .text(function(){return d.name;});
              }else{
                var top=d.name.substring(0,4);
                var bot=d.name.substring(4,d.name.length);

                d3.select(this).text(function(){return '';});

                d3.select(this).append('tspan')
                        .attr('x',0)
                        .attr('y',-7)
                        .text(function(){return top;});

                d3.select(this).append('tspan')
                        .attr('x',0)
                        .attr('y',10)
                        .text(function(){return bot;});
              }
            });

    function tick() {
      circle.attr("transform", transform1);//圆圈
      text.attr("transform", transform2);//顶点文字
      edges_line.attr('d', function(d) {
        var path='M '+d.source.x+' '+d.source.y+' L '+ d.target.x +' '+d.target.y;
        return path;
      });
      edges_text.attr('transform',function(d,i){
        if (d.target.x<d.source.x){
          bbox = this.getBBox();
          rx = bbox.x+bbox.width/2;
          ry = bbox.y+bbox.height/2;
          return 'rotate(180 '+rx+' '+ry+')';
        }
        else {
          return 'rotate(0)';
        }
      });
    }

    //设置连接线的坐标,使用椭圆弧路径段双向编码
    function linkArc(d) {
      return 'M '+d.source.x+' '+d.source.y+' L '+ d.target.x +' '+d.target.y
    }
    function dragstart(d) {
      d3.event.sourceEvent.stopPropagation();
      d3.select(this).classed("fixed", d.fixed = true);
    }
    function dragend(d) {
      d3.event.sourceEvent.stopPropagation();
      d3.select(this).classed("fixed", d.fixed = true);
    }
    //设置圆圈和文字的坐标
    function transform1(d) {
      return "translate(" + d.x + "," + d.y + ")";
    }
    function transform2(d) {
      return "translate(" + (d.x) + "," + d.y + ")";
    }
  }

  //初始化
  $.getJSON("/graph", function(json){
    var linkss = json;
    mychart_first('graphics','#graphics',linkss,'传播路径');
  });
  //全屏
  $("#fullscreen").live("click",function(){
    $("#mask").css("display","block");
    var kw = $("#keyword").val();
    $.getJSON("/graph?keyword=" + kw, function(json){
      mychart_first('mask-body','#mask-body',json,'传播路径');
    });
  });
  //搜索
  $("#search").live("click", function () {
    var kw = $("#keyword").val();
    $.getJSON("/graph?keyword=" + kw, function(json){
      mychart_first('graphics','#graphics',json,'传播路径');
    });
  })
  //下载图片
  $("#download").live("click",function(){
    // var el = d3.select("#graphics").node().children[0];
    // downLoad(el,"图谱");
    var canvas = $("#graphics svg")[0];
    //调用方法转换即可，转换结果就是data -》 base64的数据图片
    svgAsPngUri(canvas, null, function(data) {
      downloadFile("图谱.png", data);
      //$("#graphics").append('<img src="' + data + '" alt="转换失败"/>');
    });
  });

</script>
<script>
  //下载数据
  const exportTag = document.getElementById('exportTag');
  $("#export").live("click", function () {
    var kw = $("#keyword").val();
    $.getJSON("/graph?keyword=" + kw, function(jsonData){
      const arr = [];
      for(let i = 0 ; i < jsonData.length ; i++ ){
        arr.push(jsonData[i]['source'] + '\t' + jsonData[i]['rela'] + '\t' + jsonData[i]['target'] + '\n');
      }
      download(arr, '数据.txt')
      exportTag.click();
    });
  })


  function download(data, name) {
    const file = new Blob(data, {type: 'text/plain'});
    exportTag.href = URL.createObjectURL(file);
    exportTag.download = name;
  }
</script>